package com.lenovo.lcdm.team.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lenovo.lcdm.common.enums.CommonMsgEnum;
import com.lenovo.lcdm.common.enums.DeleteFlagEnum;
import com.lenovo.lcdm.common.exception.BusinessException;
import com.lenovo.lcdm.common.exception.ServiceException;
import com.lenovo.lcdm.team.common.util.ValidateUtil;
import com.lenovo.lcdm.team.dto.GroupDto;
import com.lenovo.lcdm.team.dto.GroupTreeDto;
import com.lenovo.lcdm.team.dto.OperationAuthorityDto;
import com.lenovo.lcdm.team.entity.*;
import com.lenovo.lcdm.team.enums.LoginTypeEnum;
import com.lenovo.lcdm.team.mapper.TeamGroupMapper;
import com.lenovo.lcdm.team.service.*;
import com.lenovo.lcdm.team.vo.GroupVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Group Service
 * @author xujing30
 * @since 2023-12-22
 */
@Service
public class TeamGroupServiceImpl extends ServiceImpl<TeamGroupMapper, TeamGroup> implements ITeamGroupService {

    private static final String SUB_TYPE_GROUP = "GROUP";
    private static final String SUB_TYPE_ROLE = "ROLE";

    private static final Long ROOT_GROUP_ID = 0L;
    @Autowired
    private ITeamGroupRoleService groupRoleService;

    @Autowired
    private ITeamRoleService roleService;

    @Autowired
    private ITeamUserGroupService userGroupService;

    @Autowired
    @Lazy
    private ITeamUserService userService;

    @Value("${itsm.bpo}")
    private String businessOwner;

    private static final String LENOVO_EMAIL_SUFFIX = "@lenovo.com";

    @Override
    public List<GroupTreeDto> getRoot() {
        LambdaQueryWrapper<TeamGroup> qw = new LambdaQueryWrapper<>();
        qw.eq(TeamGroup::getParentId, ROOT_GROUP_ID);
        List<TeamGroup> data = this.list(qw);
        List<GroupTreeDto> result = data.stream().map(item -> {
            GroupTreeDto dto = new GroupTreeDto();
            BeanUtils.copyProperties(item, dto);
            dto.setName(item.getGroupName());
            dto.setDescription(item.getGroupDesc());
            dto.setType(SUB_TYPE_GROUP);
            return dto;
        }).collect(Collectors.toList());
        return result;
    }

    @Override
    public GroupTreeDto getGroupInfo(Long groupId, boolean onlyRole) throws Exception {
        GroupTreeDto dto = new GroupTreeDto();
        TeamGroup group = this.getById(groupId);
        if(group == null){
            throw new ServiceException("Group does not exist!" , CommonMsgEnum.FAIL.getCode());
        }
        BeanUtils.copyProperties(group, dto);
        dto.setName(group.getGroupName());
        dto.setDescription(group.getGroupDesc());
        dto.setType(SUB_TYPE_GROUP);

        //获取child
        List<GroupTreeDto> children = this.getChildren(groupId, onlyRole);
        dto.setChildren(children);
        return dto;
    }

    /**
     * 查询group的child
     * @param groupId
     * @param onlyRole-是否只查询Role
     * @return
     */
    @Override
    public List<GroupTreeDto> getChildren(Long groupId, boolean onlyRole){
        List<GroupTreeDto> dto = new ArrayList<>();
        LambdaQueryWrapper<TeamGroupRole> qw = new LambdaQueryWrapper<>();
        qw.eq(TeamGroupRole::getGroupId, groupId);
        List<TeamGroupRole> groupRoles = groupRoleService.list(qw);

        List<TeamRole> roles = null;
        if(!CollectionUtils.isEmpty(groupRoles)){
            List<Long> roleIds = groupRoles.stream().map(TeamGroupRole::getRoleId).collect(Collectors.toList());
            roles = roleService.listByIds(roleIds);
        }

        List<TeamGroup> subGroups = null;
        if(!onlyRole){
            LambdaQueryWrapper<TeamGroup> groupQw = new LambdaQueryWrapper<>();
            groupQw.eq(TeamGroup::getParentId, groupId);
            subGroups = this.list(groupQw);
        }
        if(subGroups != null){
            List<GroupTreeDto> subGroupChildren = subGroups.stream().map(item->{
                GroupTreeDto child = new GroupTreeDto();
                child.setId(item.getId());
                child.setName(item.getGroupName());
                child.setDescription(item.getGroupDesc());
                child.setType(SUB_TYPE_GROUP);
                child.setParentId(groupId);
                return child;
            }).collect(Collectors.toList());
            dto.addAll(subGroupChildren);
        }
        if(roles != null){
            List<GroupTreeDto> roleChildren = roles.stream().map(item->{
                GroupTreeDto child = new GroupTreeDto();
                child.setId(item.getId());
                child.setName(item.getRoleName());
                child.setDescription(item.getRoleDesc());
                child.setType(SUB_TYPE_ROLE);
                child.setParentId(groupId);
                return child;
            }).collect(Collectors.toList());
            dto.addAll(roleChildren);
        }
        return dto;
    }

    @Override
    public List<TeamUser> getGroupUsers(Long groupId){
        List<TeamUser> users = new ArrayList<>();
        List<TeamUserGroup> userGroups = userGroupService.getUserByGroupId(groupId);
        if(!CollectionUtils.isEmpty(userGroups)) {
            List<Long> useIds = userGroups.stream().map(TeamUserGroup::getUserId).collect(Collectors.toList());
            users = userService.listByIds(useIds);
        }
        if(!CollectionUtils.isEmpty(users)){
            users = users.stream().filter(user->user.getDeleted().intValue() == 0).collect(Collectors.toList());
        }
        return users;
    }

    @Override
    public void deleteGroup(Long groupId){
        //删除user-group关系
        LambdaQueryWrapper<TeamUserGroup> userGroupQw = new LambdaQueryWrapper<>();
        userGroupQw.eq(TeamUserGroup::getGroupId, groupId);
        userGroupService.remove(userGroupQw);

        //删除group-role关系
        LambdaQueryWrapper<TeamGroupRole> groupRoleQw = new LambdaQueryWrapper<>();
        groupRoleQw.eq(TeamGroupRole::getGroupId, groupId);
        groupRoleService.remove(groupRoleQw);

        //删除group
        this.removeById(groupId);
    }

    @Override
    public void saveGroup(GroupDto groupDto) throws Exception{
        if(groupDto.getParentId() == null){
            groupDto.setParentId(ROOT_GROUP_ID);
        }
        validateGroup(groupDto);
        TeamGroup group = null;
        if(groupDto.getId()  == null){
            group = new TeamGroup();
            group.setCreateTime(LocalDateTime.now());
        }else{
            group = this.getById(groupDto.getId());
            if(group.getCreator() != null) {
                groupDto.setCreator(group.getCreator());
            }
        }
        BeanUtils.copyProperties(groupDto, group);
        group.setModifyTime(LocalDateTime.now());
        this.saveOrUpdate(group);
        if(!CollectionUtils.isEmpty(groupDto.getRoleIds())){
            groupRoleService.saveGroupRoles(group, groupDto.getRoleIds());
        }

    }

    private void validateGroup(GroupDto groupDto) throws Exception{
        StringBuffer buffer = new StringBuffer();
        String groupName = groupDto.getGroupName();
        String groupDesc = groupDto.getGroupDesc();


        if (StringUtils.isEmpty(groupName)) {
            buffer.append("GroupName cannot be empty!\n");
        }
        if (StringUtils.isNotEmpty(groupName) && !ValidateUtil.checkName(groupName)) {
            buffer.append("RoleName must be the combination of Latin alphabet, digit or underscore (i.e. _).\n");
        }
        List<TeamGroup> existGroups = this.getByName(groupName);
        if(!CollectionUtils.isEmpty(existGroups) && (groupDto.getId() == null || existGroups.size() > 1 || existGroups.get(0).getId().intValue() != groupDto.getId().intValue())){
            buffer.append("RoleName exists in system!\n");
        }

        if (StringUtils.isNotEmpty(groupDesc) && groupDesc.length() > 200) {
            buffer.append("GroupDesc can't longer than 200!\n");
        }
        if(buffer.length() > 0){
            throw new BusinessException(buffer.toString(), CommonMsgEnum.FAIL.getCode());
        }
    }

    /**
     * 根据角色名称查询
     * @param groupName
     * @return
     */
    private List<TeamGroup> getByName(String groupName){
        LambdaQueryWrapper<TeamGroup> qw = new LambdaQueryWrapper<>();
        qw.eq(TeamGroup::getGroupName, groupName);
        return this.list(qw);
    }

    @Override
    public void addUserGroup(TeamUserGroup userGroup) throws Exception{
        TeamGroup group = this.getById(userGroup.getGroupId());
        if(group == null){
            throw new Exception("Group does not exist!");
        }
        TeamUser user = userService.getById(userGroup.getUserId());
        if(user == null || user.getDeleted().intValue() == 1){
            throw new Exception("User does not exist!");
        }
        LambdaQueryWrapper<TeamUserGroup> userGroupQw = new LambdaQueryWrapper<>();
        userGroupQw.eq(TeamUserGroup::getGroupId, userGroup.getGroupId()).eq(TeamUserGroup::getUserId, userGroup.getUserId());
        List<TeamUserGroup> userGroups = userGroupService.list(userGroupQw);
        if(CollectionUtils.isEmpty(userGroups)){
            userGroup.setCreateTime(LocalDateTime.now());
            userGroup.setModifyTime(LocalDateTime.now());
            userGroupService.save(userGroup);
        }
    }

    @Override
    public void deleteUserGroup(Long groupId, List<Long> userIds){
        LambdaQueryWrapper<TeamUserGroup> userGroupQw = new LambdaQueryWrapper<>();
        userGroupQw.eq(TeamUserGroup::getGroupId, groupId).in(TeamUserGroup::getUserId, userIds);
        userGroupService.remove(userGroupQw);
    }

    @Override
    public void deleteGroupRole(Long groupId, Long roleId) {
        LambdaQueryWrapper<TeamGroupRole> groupRoleQw = new LambdaQueryWrapper<>();
        groupRoleQw.eq(TeamGroupRole::getGroupId, groupId).eq(TeamGroupRole::getRoleId, roleId);
        groupRoleService.remove(groupRoleQw);
    }

    @Override
    public List<GroupVo> getGroupByName(String name) {
        QueryWrapper<TeamGroup> groupQw = new QueryWrapper<>();
        if(StringUtils.isNotEmpty(name)){
            groupQw.select("group_name,group_desc").like("lower(group_name)", name.toLowerCase());
        }
        List<Map<String, Object>> map = this.listMaps(groupQw);
        List<GroupVo> groups = map.stream().map(item->{
            GroupVo groupVo = new GroupVo();
            groupVo.setGroupName(item.get("group_name").toString());
            groupVo.setBusinessOwner(businessOwner);
            groupVo.setGroupDescription(item.get("group_desc")==null?"":item.get("group_desc").toString());
            return groupVo;
        }).collect(Collectors.toList());
        return groups;
    }

    @Override
    public void operationAuthority(OperationAuthorityDto operationAuthorityDto) {
        if (StringUtils.isBlank(operationAuthorityDto.getAction()) ||
                StringUtils.isBlank(operationAuthorityDto.getItCode()) ||
                CollectionUtil.isEmpty(operationAuthorityDto.getGroupNames())) {
            throw new BusinessException("Parameter cannot be empty!", CommonMsgEnum.FAIL.getCode());
        }
        List<Long> groupIds = operationAuthorityDto.getGroupNames().stream().map(groupName -> {
            TeamGroup group = this.getByName(groupName).stream().findFirst().orElse(null);
            if (group == null) {
                throw new BusinessException("Group does not exist!", CommonMsgEnum.FAIL.getCode());
            }
            return group.getId();
        }).toList();

        if (StringUtils.equals(operationAuthorityDto.getAction(), "Add")) {
            LambdaQueryWrapper<TeamUser> qw = new LambdaQueryWrapper<>();
            qw.eq(TeamUser::getItCode, operationAuthorityDto.getItCode());
            TeamUser user = userService.list(qw).stream().findFirst().orElse(null);
            if (user == null) {
                Date now = new Date();
                user = new TeamUser();
                user.setItCode(operationAuthorityDto.getItCode());
                user.setEmail(user.getItCode() + LENOVO_EMAIL_SUFFIX);
                user.setCreator("SystemAdmin");
                user.setModifier("SystemAdmin");
                user.setLoginType(LoginTypeEnum.REMOTE.getType());
                user.setCreateTime(now);
                user.setModifyTime(now);
                user.setEnabled(1);
                user.setPassword(null);
                userService.saveOrUpdate(user);
            } else {
                if (DeleteFlagEnum.YES.getValue() == user.getDeleted()) {
                    user.setDeleted(DeleteFlagEnum.NO.getValue());
                    userService.saveOrUpdate(user);
                }
            }
            Long userId = user.getId();
            groupIds.forEach(groupId -> {
                TeamUserGroup userGroup = new TeamUserGroup();
                userGroup.setGroupId(groupId);
                userGroup.setUserId(userId);
                try {
                    this.addUserGroup(userGroup);
                } catch (Exception e) {
                    throw new BusinessException(e.getMessage(), CommonMsgEnum.FAIL.getCode());
                }
            });
        } else if (StringUtils.equals(operationAuthorityDto.getAction(), "Delete")) {
            LambdaQueryWrapper<TeamUser> qw = new LambdaQueryWrapper<>();
            qw.eq(TeamUser::getItCode, operationAuthorityDto.getItCode());
            TeamUser user = userService.list(qw).stream().findFirst().orElse(null);
            if (user == null) {
                throw new BusinessException("User does not exist!", CommonMsgEnum.FAIL.getCode());
            }
            Long userId = user.getId();
            groupIds.forEach(groupId -> this.deleteUserGroup(groupId, List.of(userId)));
        } else {
            throw new BusinessException("Action is not supported!", CommonMsgEnum.FAIL.getCode());
        }
    }
}
